#!/bin/bash
#THIS DRIVER WRITTEN BY MATTHEW VONA ARB FOR THE API T400
#FOR QUESTIONS WRITE mailto:mvona@arb.ca.gov
#UPDATE: 12/12/2011; Added data file lock handler;  
#UPDATE: 10/25/2013; Removed the low flow setting feature pre management -- MJV
#-------------------------------------------------------------
#!/usr/bin/env bash
# THIS IS THE FUNCTION CALLED IN THE EVENT THAT CTRL+C IS PRESSED
cleanup()
{
	echo "Caught Signal ... cleaning up."
	if !(rm -f Data/.locked > /dev/null); then
		exit 1
	fi
	for i in `pgrep -P ${plotterid}`; do
		kill -9 ${i}
	done
	kill -9 ${plotterid}
	echo "Done cleanup ... quitting."
	exit 0
}

###############################################################
# BEGIN DISPLAY FUNCTION : MJV 11/15/2013                     #
###############################################################
#THIS IS THE RELATIVE DIRECTORY VARIABLE 
DIR="$( cd -P "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
#########################################################################################
#                       BEGIN MAIN DISPLAY FUNCTION.                                    #
#########################################################################################
#read config file
while read line; do
	eval $line
done < Config/config
if [ -z $no_plots]; then
	bash ${DIR}/plotter.sh 1>/dev/null 2>/dev/null &
	plotterid="$!"
fi
#########################################################################################
#						END MAIN DISPLAY FUNCTION.										#
#########################################################################################

#THE DIAGNOSTIC DATA (DIAG) ROUTINE TAKES THE DATA STREAM FROM THE API 400E
#AND TESTS FOR THE RECOGNITION OF KEY WORDS IN EACH LINE OF OUTPUT.
#IF THE KEY WORD IS DETECTED ON THAT LINE, A TEST IS PERFORMED. 
#IF THE TEST SUGGESTS THAT AN ERROR CONDITION EXISTS BASED UPON THE ALLOWABLE
#RANGES SPECIFIED BY THE ARB CHECKSHEET, THE OFFENDING LINE IS WRITTEN OUT 
#TO A FILE IN THE Data DIRECTORY. THE NAME OF THE DESTINATION FILE IS
#errors{HH}. THE FILES CAN THEN BE PARSED AND UTILIZED BY ANY ALERT PROGRAM OR DAEMON.
diag()
{
this_name=$(cat ${DIR}/.usr$instance | grep -o "\-r [A-Za-z0-9]*" | awk '{ print $2 }')
thishour=`date "+%H"`
thisalert="errors$thishour"
while read DIAGS
do
awk -v low_flow=${low_flow} -v upp_flow=${upp_flow} -v izs=${izs} -v name=${this_name} '
{                                           
# TEST FOR ANY WARNING MESSAGES
if ($6 ~ /WARN/)
	{
	print name " WARNING DETECTED" $0
	}
# TEST THE TIME AGAINST DATA LOGGER
if ($4 ~ /TIME=/ && substr($1,length($1)-5,2)==32)
{
#do not run this test during the first or last three minutes of any hour
#this test will run once every hour
	#test for off an hour or more
		if (substr($4,6,2) != substr($1,length($1)-8,2))
  			{ print name " HOUR NOT MATCHING DATA LOGGER" $0 }
	#test for off more than 3 minutes. Must add one minute to the 
	#current minute value since the buffer contains values from the previous minute
   if (sqrt(((substr($4,9,2)+1) - substr($1,length($1)-5,2))^2) > 3 )
  		{ print name " MINUTE NOT MATCHING " $0 " " substr($2,5,2) " " substr($2,8,2) }	
}
# TEST THE RANGE
if ($4 ~ /RANGE=/ ) 
	{
 	if (substr($4,7) !~ 500.0)
  		{ print name " RANGE NOT MATCHING" $0 }
	}
# TEST THE FLOW RATE
if ($5 ~ /FL=/ ) 
	{
 	if ((substr($5,4)+0)<=low_flow)
 	  		{ print name " FLOW IS VERY LOW" $5 " is less than " low_flow" "$0 }
  	if ((substr($5,4)+0)>=upp_flow)
  	        { print name " FLOW IS VERY HIGH" $5 " is greater than " upp_flow" "$0}
  	if (substr($5,4) ~ /XXXX/)
  		{ print name " FLOW IS TOO LOW" $0 }
	}
	#TEST THE LAMP VOLTAGE
if ($5 ~ /LAMP=/)
	{
	if ((substr ($5,6)+0)>=59.0)
		{ print name " LAMP TEMP TOO HIGH" $0 }
	if ((substr ($5,6)+0)<=57.0)
			{ print name " LAMP TEMP TOO LOW" $0 }
	}
#TEST THE OFFSET
if ($4 ~ /OFFSET=/)
	{
	if ((substr ($4,8)+0)>= 5)
			{ print name " OFFSET TOO HIGH" $0 }
#	if ((substr ($4,7)+0)<= -5)
#		{ print name " OFFSET TOO LOW" $0 }	
	}		
#TEST THE O3 MEAS
if (/O3 MEAS=/)
	{
	if ((substr ($5,6)+0)<=2500)
		{ print name " O3 MEAS TOO LOW" $0  }
	if ((substr ($5,6)+0)>=4900)
		{ print name " O3 MEAS TOO HIGH" $0 }
	}
	#TEST THE SLOPE FOR 1 +/- .1 AND CHANGES
if (/SLOPE=/)
	{
		if ((substr ($4,7)+0) >= 1.15)
			{ print name " SLOPE TOO HIGH" $0 }
		if ((substr ($4,7)+0) <= .85)
			{ print name " SLOPE TOO LOW" $0 }
	}
#THESE TESTS ARE RUN IF THE izs=on PARAMETER HAS BEEN PASSED
if (/O3 GEN TMP=/)
		{
		if (izs == on)
			{
			if ((substr ($6,5)+0)<=45)
				{ print name " O3 GEN TEMP TOO LOW" $0 }
			if ((substr ($6,5)+0)>=51)
				{ print name " O3 GEN TEMP TOO HIGH" $0 }
			}
		}
if (/O3 DRIVE=/)
		{
		if (izs == on)
			{
			if ((substr ($5,7)+0)<=0)
				{ print name " O3 DRIVE TOO LOW" $0 }
			if ((substr ($5,7)+0)>=5000)
				{ print name " O3 DRIVE TOO HIGH" $0 }
			}
		}			
if (/O3 GEN=/)
		{
		if (izs == on)
			{
			if ((substr ($5,5)+0)<=80)
				{ print name " O3 GEN TOO LOW" $0 }
			if ((substr ($5,5)+0)>=5000)
				{ print name " O3 GEN TOO HIGH" $0 }
			}
		}
}' >> "Data/$thisalert"
done
}
#BEGIN WRITE
#THIS FUNCTION HANDELS READ RIGHT HANDSHAKES BETWEEN VARIOUS DRIVERS
#IN GENERAL, THE DATA IS PLACED IN THE VARIABLE $TIMESTAMP PRIOR TO CALL
#NEXT, IT OPENS THE DATA DIRECTORY AND LOOKS FOR THE .locked FILE
#IF IT EXISTS, IT HANGS OUT UNTIL IT DOESN'T EXIST. AT THAT POINT
#IT CREATES A .locked FILE, AND APPENDS TO THE DATA RECORD.
#FINALLY IT REMOVES THE .locked FILE IT HAS CREATED.
#THIS FUNCTION ALSO PARSES THE .DISABLED FILE IN THE DRIVER FOLDER
#AND TAKES A CHANNEL OFF LINE BY PREPENDING --DISABLED-- TO EACH LINE OF CONTENT IN THE DATA DIRECTORY
#written by MJV
write()
{
#CONFIRM THAT THERE IS NO HIDDEN FILE CALLED '.locked'
	while [ -e "Data/.locked" ];
		do
			sleep .2;
		done	
	echo "LOCKED" > "Data/.locked";
	#PARSE THE .disabled File
	channel_status=`cat "${DIR}/.disabled${instance}"`
	#IF THE FILE DOSEN'T EXIT OR IS NOT ACCESIBLE
	#MAKES SURE A VALUE OF 0 IS USED
	
	source sys_resources/translate_code.sh displaycode $channel_status rawcode
		
	if !(echo "$data" | sed 's/^/'$timestamp'/g'| sed 's/BOX TEMP=/O3BOXTEMP=/g' | sed 's/$/'$target_value'/g'| tee -a "Data/$datafile" | diag); then
		echo "GENERAL FAILURE WRITING TO RECORD: $#";
		cleanup;
	fi

	if !(rm -f "Data/.locked" > /dev/null); then
		{
			echo "GENERAL FAILURE REMOVING LOCK: $#";
			exit 1;	
		}
	fi
	if !(timestamp="" && data=""); then
		{
		echo "GENERAL FAILURE CLEARING WRITE BUFFER: $#";
		cleanup;
		}
	fi	
}

#THIS IS THE ACTUAL COMMUNICATION WITH THE INSTRUMENT
#THE TIMER CONTROL. AT THE BEGINING IT TESTS FOR THE 
#EXISTENCE IF TOGGLE VARRIABLES WHICH ARE UTILIZED BOTH BY
#THE COMM ROUTINE runit() AND THE DIAGNOSTICS DATA ROUTINE
#diag()
runit()
{
#IF THE -d, -C, or -p variable is set to on, then append those parameters
#to the sjinn communication string. If the -i parameter is passed, diag will
#test for IZS specific routines.
if [ "$izs_val" = "on" ]; then
	izs="on"
fi
if [ "$dtr_val" = "on" ]; then
 	dtr="--dtr" 
fi
if [ "$ctr_val" = "on" ]; then
 	ctr="--ctr"
fi
if [ "$p8n1_val" = "on" ]; then
 	p8n1="-p8n1"
fi
#Date stamp
today=`date "+%Y-%d-%m"`
timestamp=$rprt_nm::`date "+%m-%d-%Y_%H:%M"`:::
#THIS IS THE NAME OF THE DATA FILE RIGHT NOW
datafile="$saroad-$today"
#TALK TO THE INSTRUMENT, PRE-APPEND THE TIMESTAMP AND REPORTING NAME TO EACH
#LINE OF OUTPUT AND tee TO THE DATA FILE, then pass to the diag routine.
cmnd2="T $id_nmbr $cmnd1\r"
#OBTAIN THE DATA
data=`timeout 10 sjinn -b$bd_rt -d $dvc_prt -s "$cmnd2" -r$rsp_lngth -w$wt_rsp1 $p8n1 $dtr $ctr\
 | grep -v "^$"` 
sleep 10
#OBTAIN THE ERRORS
error_data=`timeout 10 sjinn -d $dvc_prt -b$bd_rt -r1000 -w4 $p8n1 $dtr $ctr -s "W ${id_nmbr} LIST\r"`
#sleep 2
#CLEAR THE OUTPUT STREAM
clear=`timeout 10 sjinn -d $dvc_prt -b$bd_rt -r2 $p8n1 $dtr $ctr -w4 -s "W ${id_nmbr} CLEAR ALL\r"`
sleep 2
data="${data}\r${error_data}"
#wait $!
write

		#|     V [id] MODE                    (Print current instrument mode) |
		#|     V [id] CURR_TIME[=HH:MM]           (Print/set instrument time) |
		#|     V [id] CURR_DATE[=MM/DD/YYYY]      (Print/set instrument date) |
		#----------------------------------------------------------------------
		#V  039:14:16  0400  SAMPLE
		#V  039:14:17  0400  ZERO CAL M
		#V  039:14:17  0400  SPAN CAL M
		###################################################################################
		#                                                                                 # 
		#          VIRTUAL ENVIRONICS CHANNEL FOR IZS    -MJV Feb 8, 2012                 # 
		#                                                                                 #
		###################################################################################	
		if [ "$izs_val" = "on" ]; then
			#Variable ::Determine the cal mode of the instrument and stored the response		
			cal_data=`timeout 10 sjinn -b$bd_rt -d $dvc_prt -s "V ${id_nmbr} mode\r" -r45 -w1 $p8n1 $dtr $ctr | grep -v "^$"`
			text="VIRTUAL ENVIRONICS CAL CHANNEL CREATED BY $0"
			#wait $!			
			#cal_data="${cal_data} ${text}"
			#Variable ::This variable will be used to stamp cal cycles and calculate recovery periods			
			cal_stamp=`date '+%s'`
			#CHECK THE STRING FOR THE PRESENCE OF A ZERO CAL
			if [ `echo $cal_data | grep -o 'ZERO CAL' | wc -w` = "2" ]; then
				value="11"			
				echo ${cal_stamp}>"${DIR}/.last_cal"
			#CHECK THE STRING FOR THE PRESENCE OF SPAN			
			elif [ `echo $cal_data | grep -o 'SPAN CAL' | wc -w` = "2" ]; then
				value="12"
				#Write the time stamp of this cal
				echo ${cal_stamp}>"${DIR}/.last_cal"
			#CHECK FOR LOW SPAN
			elif [ `echo $cal_data | grep -o 'LOW CAL' | wc -w` = "2" ]; then
				value="13"
				#Write the time stamp of this cal
				echo ${cal_stamp}>"${DIR}/.last_cal"
			#CHECK FOR OUT OF CAL
			elif [ `echo $cal_data | grep -o 'SAMPLE' | wc -w` = "1" ]; then
				#We are not calibrating, so check the .last_cal 
				#if the number of minutes since the last cal is <= $recov_tim (DEFAULT 5)
				#then use a cal value of 19 to indicate recovery
				#Variable ::Number of seconds between 1970 and the last calibration				
				last_cal=`cat ${DIR}/.last_cal`
				#Variable ::Number of seconds in $recov_tim passed from interface or default				
				recov_tim=300	
				#Variable ::Subtraction of current time minus last cal time 			
				recov_duration=`echo "${cal_stamp} - ${last_cal}" | bc`	
				
				if [ ${recov_duration} -lt ${recov_tim} ]; then
					#SET THE CAL VALUE TO RECOV SO CARBLOGGER WILL MARK INVALID				
						value="19"
					else				
					#SET THE CAL VALUE TO 0				
						value="0"		
				fi					
			fi
			timestamp="environics::`date "+%m-%d-%Y_%H:%M"`:::${cal_data}:::cal=${value}"
			echo $timestamp >> "Data/$datafile"		
		fi
}
#THIS IS THE FUNCTION RUN IN THE EVENT THAT THE REQUIRED PARAMETERS
#ARE NOT ENTERED.
usage()
{
clear
cat << EOF
usage: $0 options
OPTIONS:
-h	display help
-n	the device identifier
-i	Activate Diagnostics for IZS model [e.g. on, off] 
-r	rprt_nm [rn]
-d	dvc_prt [eg, /dev/ttyS0, /dev/ttyUSB1] 
-b	bd_rt [2400,4800,9600,etc.]
-c	cmnd1 (e.g, LIST ALL\r ***DO NOT USE T LIST ALL***)
-w	wt_rsp1 [seconds]
-l	rsp_lngth [characters]
-s	SAROAD number of the station
-t	[on/off] (OPTIONAL)
-C	[on/off] (OPTIONAL)
-p	[on/off] (OPTIONAL)
-U	Uppler flow limit alert (OPTIONAL)
-L	Lower flow limit alert (OPTIONAL)
-R	number of minutes to invalidate after auto or manual cals
Last updated 3/26/2012
EOF
}
#HELP FUNCTION GIVES BACKGROUND ON THIS DRIVER
help_me ()
{
clear
cat .help
exit 0
}
def_opts ()
{
clear
cat .options
}
###############################################################
#							      +
#TEST FOR INCOMPATIBLE INSTRUMENT CONFIGURATIONS 2/10/2012    +
#							      +
###############################################################
# THE FOLLOWING CONDITIONS CANNOT BE ALLOWED WHEN USING THIS 
# DRIVER
#	1) environics 9100 driver cannot be running
#	2) the dms.cfg file must have the following entries
#	O3= O3 11 12 13 1 1
#	RECOV= RECOV 19 -- -- 1 1
#	3) environics9100 should not be in the Config/config file. 
# THIS SECTION MAKES THE NECESSARY CHECKS AND CHANGES
################################################################
if [ `ps -fe | grep testd | grep environics | awk '{ print $2 }'` > "1" ]; then
	
	echo "CANNOT BE RUN WHILE ENVIRONICS DRIVER IS IN USE...EXITING"
	echo "API 400E IZS DRIVER TERMINATED...CANNOT BE RUN WHILE ENVIRONICS DRIVER IS IN USE...EXITING" >>\
 "Data/CARBLogger.log"	
	exit
	
fi
#CONFIRM THE CONFIG FILE
sed -i '/^O3=[\ -,0-9,a-z,A-Z]*$/d' sys_resources/dms.cfg
echo 'O3= O3 11 12 13 0 1' >> sys_resources/dms.cfg
sed -i '/^RECOV=[\ -,0-9,a-z,A-Z]*$/d' sys_resources/dms.cfg
echo 'RECOV= RECOV 19 -- -- 0 1' >> sys_resources/dms.cfg
sed -i '/^O3BOXTEMP=[\ -,0-9,a-z,A-Z]*$/d' sys_resources/dms.cfg
echo 'O3BOXTEMP= O3BOXTEMP -- -- -- 0 9' >> sys_resources/dms.cfg
##CLEAR ALL VARIABLES               
rprt_nm= ; dvc_prt= ; bd_rt= ; cmnd1= ; wt_rsp1= ; bd_rt= ; rsp_lngth= ; izs_val= ;
dvc_prt= ; frqncy= ; dtr= ; ctr= ; p8n1= ; izs= ; saroad= ; p8n1= ; id_nmbr= ; 
upp_flow=880 ; low_flow=550 ; instance=1; recov_tim=5;
##INITIALIZE THE BUFFER
##return='NOT WRITTEN TO'
##loop through all variables and parameters
##Note that the position of the colon indicates the presence
##of a parameter to collect. The h parameter simply
##returns the help message.

while getopts “hn:i:r:s:d:b:c:w:l:t:C:p:U:L:I:R:” OPTION
do
case $OPTION in
U)
upp_flow=$OPTARG
;;
L)
low_flow=$OPTARG
#PER R SMITH; SETTING LOW FLOW ALERT TO 550
#GLOBALLY. THIS WILL OVERWRITE ANY VALUE THE
#END USER CONFIGURES ON THEIR CARBLOGGER
#MENU.
low_flow=550
;;
n)
id_nmbr=$OPTARG
;;
a)
abs_path=$OPTARG
;;
i)
izs_val=$OPTARG
;;
I)
instance=$OPTARG
;;
s)
saroad=$OPTARG
;;
r)
rprt_nm=$OPTARG
;;
d)
dvc_prt=$OPTARG
;;
b)
bd_rt=$OPTARG
;;
c)
cmnd1=$OPTARG
;;
w)
wt_rsp1=$OPTARG
;;
l)
rsp_lngth=$OPTARG
;;
t)
dtr_val=$OPTARG
;;
C)
ctr_val=$OPTARG
;;
R)
recov_tim=$OPTARG
;;
p)
p8n1_val=$OPTARG
;;
h)
help_me
exit 1
;;

*)
usage
exit 1
;;
esac
done
#CHECK FOR THE CRITICAL PARAMETERS, IF THEY ARE NOT PRESENT THEN
#RUN THE USAGE FUNCTION WHICH RETURNS THE INSTRUCTIONS FOR PROPER USAGE
#MUST HAVE r n d b c w l i s
if [ -z "$id_nmbr" ]
	then
		{
			echo "You must specify the ID# (-n) of the API 400E IZS. This can be found by"
			echo "entering and reading setup>vars>[61 Machine_ID]"
			exit;		
		}
elif [ -z "$rprt_nm" ] || [ -z "$dvc_prt" ] || [ -z "$bd_rt" ] || [ -z "$cmnd1" ] ||
[ -z "$wt_rsp1" ] || [ -z "$rsp_lngth" ] || [ -z "$saroad" ] || [ -z "$izs_val" ] 
	then
		{
			usage
			exit
		}
fi
	#INITIALIZED THE RAN VARIABLE. THIS IS USED TO TOGGLE THE TIMER GATE
ran=0
	#TRAP CTRL+C
trap cleanup 1 2 3 6 15
	#SETUP THE OPERATING LOOP
while :
do
second=`date "+%S"`
if [ "$second" -eq "00" ] && [ "$ran" -eq "0" ]; then
	{
	#RUN THE RUNIT FUNCTION
		runit
	#TOGGLE THE RAN VARIABLE SO THAT THE PROCESS WILL NOT REPEAT
	#UNTIL THE BEGINNING OF THE NEXT MINUTE.
		ran=1
	#WAIT FOR COMPLETION OF THE RUNIT FUNCTION AND THE VARIABLE
	#SET
		sleep 1
	}
	#AND WAIT ONE SECOND BEFORE CHECKING AGAIN
else 
	{
	#WAIT ONE SECOND
		sleep .5
	#SET RAN TO 0
		ran=0
	}
fi
done;
exit;

